package com.ejlchina.tool.viewmodel

import android.annotation.SuppressLint
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.lifecycle.ViewModel
import com.ejlchina.tool.bean.BleDeviceBean
import com.ejlchina.tool.bean.IndicatorDataBean
import com.ejlchina.tool.bean.IndicatorDeviceDataBean
import com.ejlchina.tool.bean.IndicatorResultBean
import com.ejlchina.tool.constant.BLE_CHARACTER_WRITE_FULL
import com.ejlchina.tool.constant.BLE_SERVICE_FULL
import com.ejlchina.tool.enums.FirmwareUpgradeResultEnum
import com.ejlchina.tool.singleton.AES_128ECB
import com.ejlchina.tool.singleton.BleWriteException
import com.ejlchina.tool.singleton.GlobalAction
import com.ejlchina.tool.singleton.GlobalAction.connectFailureFlow
import com.ejlchina.tool.singleton.GlobalAction.connectSuccessFlow
import com.ejlchina.tool.singleton.GlobalAction.disConnectFlow
import com.ejlchina.tool.singleton.GlobalAction.mtuSetFailureFlow
import com.ejlchina.tool.singleton.GlobalAction.mtuSetSuccessFlow
import com.ejlchina.tool.singleton.GlobalAction.notifyDataFlow
import com.ejlchina.tool.singleton.GlobalAction.notifyResultFlow
import com.ejlchina.tool.singleton.GlobalAction.scanDeviceFinishFlow
import com.ejlchina.tool.singleton.GlobalAction.scanDeviceResultFlow
import com.ejlchina.tool.singleton.GlobalAction.startScanIfSuccessFlow
import com.ejlchina.tool.singleton.GlobalConnectObject
import com.ejlchina.tool.singleton.GlobalParams
import com.ejlchina.tool.util.COMMAND_FIRMWARE_UPGRADE_RESULT_UPWARD
import com.ejlchina.tool.util.COMMAND_TOKEN_DOWNWARD
import com.ejlchina.tool.util.DeviceUtil
import com.ejlchina.tool.util.LOG_TAG
import com.ejlchina.tool.util.RELOAD_AND_UPGRADE_DOWNLOAD
import com.ejlchina.tool.util.RELOAD_AND_UPGRADE_UPLOAD
import com.ejlchina.tool.util.bytearray.hex2ByteArray
import com.ejlchina.tool.util.bytearray.toHexString
import com.ejlchina.tool.util.getDownwardCommand
import com.ejlchina.tool.util.getUpwardData
import com.ejlchina.tool.util.showToast
import com.huyuhui.fastble.BleManager
import com.huyuhui.fastble.callback.BleGattCallback
import com.huyuhui.fastble.callback.BleMtuChangedCallback
import com.huyuhui.fastble.callback.BleNotifyCallback
import com.huyuhui.fastble.callback.BleScanCallback
import com.huyuhui.fastble.callback.BleWriteCallback
import com.huyuhui.fastble.data.BleDevice
import com.huyuhui.fastble.exception.BleException
import com.huyuhui.fastble.queue.operate.SequenceWriteOperator

const val PRIORITY_WRITE_DEFAULT = 500
const val PRIORITY_WRITE_HIGH = 10
@SuppressLint("MissingPermission")
class MainViewModel : ViewModel() {

    private val bleWriteCallback = object :BleWriteCallback(){
        override fun onWriteFailure(
            bleDevice: BleDevice,
            characteristic: BluetoothGattCharacteristic?,
            exception: BleException,
            current: Int,
            total: Int,
            justWrite: ByteArray?,
            data: ByteArray?,
            isTotalFail: Boolean
        ) {
            Log.i(LOG_TAG, "write failure--${data?.toHexString(false)} ----${exception.description}")
            GlobalAction.writeFailureFlow.tryEmit(BleWriteException(bleDevice,exception))
        }

        override fun onWriteSuccess(
            bleDevice: BleDevice,
            characteristic: BluetoothGattCharacteristic,
            current: Int,
            total: Int,
            justWrite: ByteArray,
            data: ByteArray
        ) {
            Log.i(LOG_TAG, "write success--${bleDevice.mac} --${justWrite.toHexString(true)}")
        }

    }
    @SuppressLint("MissingPermission")
    fun turnOnIndicate(bleDevice: BleDevice) {
        BleManager.notify(bleDevice,
            DeviceUtil.getBleService(bleDevice)?.uuid.toString(),
            DeviceUtil.getBleReadCharacteristic(bleDevice)?.uuid.toString(), object :
                BleNotifyCallback() {
                override fun onNotifySuccess(bleDevice: BleDevice, characteristic: BluetoothGattCharacteristic) {
                    Log.i(LOG_TAG,"Notify打开成功")
                    GlobalConnectObject.getDeviceFromMac(bleDevice.mac)?.let {
                        notifyResultFlow.tryEmit(IndicatorResultBean(BleDeviceBean(bleDevice, isEncrypt = it.isEncrypt), true))
                    }
                }

                override fun onNotifyFailure(bleDevice: BleDevice, characteristic: BluetoothGattCharacteristic?, exception: BleException) {
                    GlobalConnectObject.getDeviceFromMac(bleDevice.mac)?.let {
                        notifyResultFlow.tryEmit(IndicatorResultBean(BleDeviceBean(bleDevice, isEncrypt = it.isEncrypt), false))
                    }
                }

                override fun onCharacteristicChanged(bleDevice: BleDevice, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
                    GlobalConnectObject.getDeviceFromMac(bleDevice.mac)?.let { bleDeviceBean ->
                        val cipherData: ByteArray
                        if (bleDeviceBean.isEncrypt){
                            Log.i(LOG_TAG, "indication--密文--${bleDevice.mac.substringAfterLast(":")}-${data.toHexString()}")
                            cipherData = AES_128ECB.decrypt(bleDevice.mac,data)
                            Log.i(LOG_TAG, "indication--明文---${bleDevice.mac.substringAfterLast(":")}-${cipherData.toHexString()}")
                        } else {
                            cipherData = data
                            Log.i(LOG_TAG, "indication--无加密---${bleDevice.mac.substringAfterLast(":")}-${cipherData.toHexString()}")
                        }
                        if (cipherData.toHexString(false).startsWith(COMMAND_FIRMWARE_UPGRADE_RESULT_UPWARD)
                            || cipherData.toHexString(false).startsWith(RELOAD_AND_UPGRADE_UPLOAD)){
                            GlobalConnectObject.getDeviceFromMac(bleDevice.mac)?.let {
                                handleResultCommand(IndicatorDeviceDataBean(BleDeviceBean(bleDevice, isEncrypt = it.isEncrypt), cipherData))
                            }
                        } else {
                            GlobalConnectObject.getDeviceFromMac(bleDevice.mac)?.let {
                                notifyDataFlow.tryEmit(IndicatorDeviceDataBean(BleDeviceBean(bleDevice, isEncrypt = it.isEncrypt), cipherData))
                            }
                        }
                    }

                }

                override fun onNotifyCancel(bleDevice: BleDevice, characteristic: BluetoothGattCharacteristic) {

                }
            })
    }

    private fun handleResultCommand(indicatorDataBean: IndicatorDeviceDataBean){
        val resultDataString = indicatorDataBean.resultData.toHexString(false)
        val dataByteArray = getUpwardData(indicatorDataBean.resultData)
        when (resultDataString.substring(0,8)){
            COMMAND_FIRMWARE_UPGRADE_RESULT_UPWARD -> {
                if (dataByteArray[0].toInt() == 1){
                    handleFileUploadResult(indicatorDataBean.bleDevice,dataByteArray)
                }
            }
            RELOAD_AND_UPGRADE_UPLOAD -> {
                showToast("设备即将重启", duration = Toast.LENGTH_LONG)
            }
        }
    }

    private fun handleFileUploadResult(device: BleDeviceBean, dataByteArray: ByteArray) {
        val result = dataByteArray[dataByteArray.size - 1].toInt()
        if (result == FirmwareUpgradeResultEnum.SUCCESS.resultCode) {
            writeByQueue(device, getDownwardCommand(device, RELOAD_AND_UPGRADE_DOWNLOAD, "01"),false,
                isForFileUpload = false,
                priority = PRIORITY_WRITE_DEFAULT,
                writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
            )
        } else {
            GlobalParams.firmwareUpgradeList.find { it.bleDevice.mac == device.bleDevice.mac }?.let { upgradeDevice ->
                upgradeDevice.upgradeResult = -1F
            }
            GlobalAction.firmwareUpgradeMap[device.bleDevice.mac]?.tryEmit(true)
            showToast(FirmwareUpgradeResultEnum.getResultStringFromResultCode(result).resultString)
        }
    }

    @SuppressLint("MissingPermission")
    fun scanDevice(){
        BleManager.scan(object : BleScanCallback{

            override fun onScanStarted(success: Boolean) {
                Log.i("Hengliang","start scan---${success}")
                startScanIfSuccessFlow.tryEmit(success)
            }

            override fun onFilter(bleDevice: BleDevice): Boolean {
                return true
            }

            override fun onLeScan(oldDevice: BleDevice, newDevice: BleDevice, scannedBefore: Boolean) {
                Log.i("Hengliang","scan find device---${newDevice.mac}---${newDevice.isConnected}----${oldDevice.mac}---${oldDevice.isConnected}")
                if (!scannedBefore) {
                    scanDeviceResultFlow.tryEmit(newDevice)
                }
            }

            override fun onScanFinished(scanResultList: List<BleDevice>) {
                Log.i("Hengliang","finish scan---${scanResultList.size}")
                scanDeviceFinishFlow.tryEmit(true)
            }
        })
    }

    @SuppressLint("MissingPermission")
    fun setMtu(bleDevice: BleDevice, mtuValue: Int){
        BleManager.setMtu(bleDevice,mtuValue,object : BleMtuChangedCallback(){
            override fun onSetMTUFailure(bleDevice: BleDevice, exception: BleException) {
                mtuSetFailureFlow.tryEmit(exception)
            }

            override fun onMtuChanged(bleDevice: BleDevice, mtu: Int) {
                Log.i(LOG_TAG,"Mtu设置成功---大小为：${mtu}")
                mtuSetSuccessFlow.tryEmit(bleDevice)
            }

        })
    }

    @SuppressLint("MissingPermission")
    fun connectDevice(scanDeviceBean: BleDeviceBean){
        BleManager.connect(bleDevice = scanDeviceBean.bleDevice,bleGattCallback = object : BleGattCallback(){
            override fun onStartConnect(bleDevice: BleDevice) {
                Log.i(LOG_TAG,"connect start")
            }

            override fun onConnectCancel(bleDevice: BleDevice, skip: Boolean) {

            }

            override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
                Log.i(LOG_TAG,"connect failed--${exception.description}")
                connectFailureFlow.tryEmit(exception)
            }

            override fun onConnectSuccess(bleDevice: BleDevice, gatt: BluetoothGatt?, status: Int) {
                Log.i(LOG_TAG,"connect success ${bleDevice.mac}")
                connectSuccessFlow.tryEmit(scanDeviceBean)
            }

            override fun onDisConnected(isActiveDisConnected: Boolean, device: BleDevice, gatt: BluetoothGatt?, status: Int) {
                Log.i(LOG_TAG,"disconnect device ${device.mac}")
                disConnectFlow.tryEmit(device)
            }

        })
    }

    @SuppressLint("MissingPermission")
    fun disconnectDevice(scanDeviceBean: BleDeviceBean){
        BleManager.disconnect(scanDeviceBean.bleDevice)
    }


    fun writeByQueue(bleDevice: BleDeviceBean, data: ByteArray, continuous: Boolean = true,isForFileUpload: Boolean = false,priority: Int, writeType: Int) {
        val cipherData: ByteArray
        if (bleDevice.isEncrypt){
            Log.i(LOG_TAG, "write start 原文--${bleDevice.bleDevice.mac} --${data.toHexString(true)}")
            cipherData = AES_128ECB.encrypt(bleDevice.bleDevice.mac,data,data.toHexString(false).startsWith(COMMAND_TOKEN_DOWNWARD))
            Log.i(LOG_TAG, "write start 密文--${bleDevice.bleDevice.mac} --${cipherData.toHexString(true)}")
        }else {
            Log.i(LOG_TAG, "write start 无加密--${bleDevice.bleDevice.mac} --${data.toHexString(true)}")
            cipherData = data
        }

        val bluetoothGattCharacteristic = DeviceUtil.getBleWriteCharacteristic(bleDevice.bleDevice,isForFileUpload)
        bluetoothGattCharacteristic?.let {
            val sequenceWriteOperator =
                SequenceWriteOperator.Builder().serviceUUID(it.service.uuid.toString())
                    .priority(PRIORITY_WRITE_DEFAULT)
                    .characteristicUUID(it.uuid.toString()).data(cipherData).writeType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
                    .bleWriteCallback(bleWriteCallback)
                    .split(false)
                    .continuous(continuous)
                    .delay(200)
                    .timeout(500)
                    .build()
            if (!BleManager.addOperatorToQueue(
                    bleDevice.bleDevice,
                    sequenceBleOperator = sequenceWriteOperator
                )
            ) {
                Log.i(LOG_TAG, "add to query fail")
                bleWriteCallback.onWriteFailure(
                    bleDevice.bleDevice,
                    it,
                    BleException.OtherException(description = "write to queue failed"),
                    justWrite = cipherData
                )
            }
        }
    }

    fun writeFile(bleDevice: BleDeviceBean,data: String){
        val bluetoothGattCharacteristic = DeviceUtil.getBleWriteCharacteristic(bleDevice.bleDevice,true)
        val cipher = if (bleDevice.isEncrypt) AES_128ECB.encrypt(bleDevice.bleDevice.mac,data.hex2ByteArray(),false) else data.hex2ByteArray()
        bluetoothGattCharacteristic?.let {
            Log.i(LOG_TAG,"服务--${bluetoothGattCharacteristic.uuid}")
            BleManager.write(bleDevice.bleDevice, bluetoothGattCharacteristic.service.uuid.toString(),
                bluetoothGattCharacteristic.uuid.toString(),cipher,false,0,false,0,bleWriteCallback,BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
        }


    }

    fun getAllConnectDevices(): List<BleDevice>{
        return BleManager.getAllConnectedDevice()
    }

    fun getAllConnectDevicesCount(): Int{
        return getAllConnectDevices().size
    }

    fun getAllDeviceMacString(): String{
        val stringBuilder = StringBuilder()
        val deviceList = getAllConnectDevices()
        for ((index,item) in deviceList.withIndex()){
            stringBuilder.append(item.mac)
            if (index != deviceList.size - 1){
                stringBuilder.append(",")
            }
        }
        return  stringBuilder.toString()
    }

    fun findBleDeviceForConnectDevice(mac: String) :BleDevice?{
        return getAllConnectDevices().find { it.mac == mac }
    }
}